/**
 * License would add here
 */

#ifndef UTILS_JS_STRING_H
#define UTILS_JS_STRING_H

#include <csignal>
#include <cstring>
#include <string>

#ifndef OHOS_PLATFORM
#include <node_api.h>
#else
#include <napi/native_api.h>
#endif

#include "js_value_base.h"
#include "macros.h"

#define IS_STRING(type) VALUE_TYPE_CHECK(IsString(), "string", (type))

class JSString : public JSValue {
public:
    JSString(const napi_env& env)
        : JSValue(env)
    {
        napi_create_string_utf8(env, "", 0, &value_);
        UpdateType();
    }
    JSString(const napi_env& env, const char* str)
        : JSValue(env)
    {
        napi_create_string_utf8(env, str, strlen(str), &value_);
        UpdateType();
    }
    JSString(const napi_env& env, const char* str, const size_t& len)
        : JSValue(env)
    {
        napi_create_string_utf8(env, str, len, &value_);
        UpdateType();
    }
    JSString(const napi_env& env, const std::string& str)
        : JSValue(env)
    {
        napi_create_string_utf8(env, str.c_str(), str.length(), &value_);
        UpdateType();
    }
    JSString(const napi_env& env, const napi_value& value)
        : JSValue(env, value)
    {
        IS_STRING(type_);
    }
    JSString(const JSValue& value)
        : JSValue(value)
    {
        IS_STRING(type_);
    };

    inline operator std::string() const { return GetValue(); }

    inline std::string GetValue() const
    {
        size_t length = 0;
        napi_get_value_string_utf8(env_, value_, nullptr, 0, &length);
        std::string result(length + 1, 0);
        napi_get_value_string_utf8(
            env_, value_, const_cast<char*>(result.data()), result.length(), &length);
        return result;
    }

    inline size_t Length() const { return length_; }

    inline void operator=(std::string value)
    {
        napi_create_string_utf8(env_, value.c_str(), value.length(), &value_);
    }
    inline void operator=(const char* value)
    {
        napi_create_string_utf8(env_, value, strlen(value), &value_);
    }

#define DEFINE_ASSIGNMENT_METHOD(type)                                           \
    inline void operator+=(type value)                                           \
    {                                                                            \
        std::string string = GetValue();                                         \
        string += value;                                                         \
        length_ = string.length();                                               \
        napi_create_string_utf8(env_, string.c_str(), string.length(), &value_); \
    }                                                                            \
    inline JSString operator+(type value)                                        \
    {                                                                            \
        std::string string = GetValue();                                         \
        string += value;                                                         \
        return JSString(env_, string);                                           \
    }

    DEFINE_ASSIGNMENT_METHOD(std::string)
    DEFINE_ASSIGNMENT_METHOD(const char*)
    DEFINE_ASSIGNMENT_METHOD(JSString)
#undef DEFINE_ASSIGNMENT_METHOD
    inline void operator+=(napi_value value) { *this += JSString(env_, value); }
    inline JSString operator+(napi_value value) { return *this + JSString(env_, value); }

private:
    size_t length_ { 0 };
};

inline JSString JSValue::ToString() const
{
    JSValue object(env_);
    napi_coerce_to_string(env_, value_, &object);
    object.UpdateType();
    return object.As<JSString>();
}

inline std::ostream&
JSValue::operator<<(std::ostream& out)
{
    out << ToString().GetValue();
    return out;
}

#undef IS_STRING
#endif /* UTILS_JS_STRING_H */
